#![cfg_attr(
  all(not(debug_assertions), target_os = "windows"),
  windows_subsystem = "windows"
)]

mod cmd;
mod menu;

use serde::{Deserialize, Serialize};
use std::env;
use std::path::PathBuf;
// use tauri_plugin_log::{LogLevel, LogTarget, LoggerBuilder};
//use tauri_plugin_sqlite;
// use tauri_plugin_positioner::{WindowExt, Position};

use async_graphql::{Request, Schema, Variables};
use tauri::{
  async_runtime, http::ResponseBuilder, CustomMenuItem, RunEvent,
  GlobalShortcutManager, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowBuilder,
  WindowUrl,
};

#[derive(Serialize)]
struct Reply {
  data: String,
}

#[derive(Serialize, Deserialize)]
struct HttpPost {
  foo: String,
  bar: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct GraphqlQuery {
  query: String,
  variables: serde_json::Value,
}

#[derive(Serialize)]
struct HttpReply {
  msg: String,
  request: HttpPost,
}

fn main() {
  let environment: String = match env::var("ENVIRONMET") {
    Ok(v) => v,
    _ => "PRODUCTION".to_string(),
  };

  let sql_log_path: String = match env::var("SQL_LOG_PATH") {
    Ok(v) => v,
    _ => "./data/logs/".to_string(),
  };

  let database_url: String = match env::var("DATABASE_URL") {
    Ok(v) => v,
    _ => "./data/sqlite/ebmstore".to_string(),
  };

  // let vergen_build_timestamp: String =
  //   env::var("VERGEN_BUILD_TIMESTAMP").expect("what's VERGEN_BUILD_TIMESTAMP?!");
  // let vergen_git_semver: String =
  //   env::var("VERGEN_GIT_SEMVER").expect("what's VERGEN_GIT_SEMVER?!");

  // println!("main.rs Build Timestamp: {}", vergen_build_timestamp);
  // println!("main.rs Build Git Semver: {}", vergen_git_semver);

  let port = match environment.as_str() {
    "DEV" => 8181,
    _ => portpicker::pick_unused_port().expect("failed to find unused port"),
  }; 

  println!("main.rs SQL_LOG_PATH: {}", sql_log_path);
  println!("main.rs DATABASE_URL: {}", database_url);
  println!("main.rs port: {}", port);

  // start composing a new Builder chain
  #[allow(unused_mut)]
  let mut app = tauri::Builder::default()
    // .setup(|app| {
    //   let mut win = app.get_window("main").unwrap();
  
    //   // Call this method from a different thread
    //   tauri::async_runtime::spawn(async move {
    //     let _ = win.move_window(Position::TopRight);
    //   });
      
    //   Ok(())
    // })
    .plugin(actix_graphql::Localhost::new(
      port,
      &sql_log_path,
      &database_url,
    ))
    //.plugin(LoggerBuilder::new([LogTarget::Folder("./".into()), LogTarget::Stdout]).build())
    //.plugin(
    //  tauri_plugin_sqlite::LoggerBuilder::new([tauri_plugin_sqlite::LogTarget::Folder(
    //    "./".into(),
    //  )])
    //  .build(),
    //)
    .register_uri_scheme_protocol("graphql", move |_app_handle, request| {
      let request_str = String::from_utf8(request.body().to_vec()).unwrap();

      println!(
        "tauri custom protocol graphql tauri::http::Request={:?},\n request_str={}\n",
        request, request_str
      );

      let request_gql: GraphqlQuery = serde_json::from_slice(request.body()).unwrap();
      println!(
        "\n\ntauri custom protocol graphql request_gql={:?} query={} variables={}\n\n",
        request_gql,
        request_gql.query,
        request_gql.variables.to_string()
      );

      //let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
      let query = Request::new(request_gql.query.to_owned())
        .variables(Variables::from_json(request_gql.variables));

      ResponseBuilder::new()
        .mimetype("application/json")
        .header("Access-Control-Allow-Origin", "*")
        .status(200)
        .body(serde_json::to_vec(&[{}, {}])?)
    })
    .register_uri_scheme_protocol("customprotocol", move |_app_handle, request| {
      if request.method() == "POST" {
        let request: HttpPost = serde_json::from_slice(request.body()).unwrap();

        return ResponseBuilder::new()
          .mimetype("application/json")
          .header("Access-Control-Allow-Origin", "*")
          .status(200)
          .body(serde_json::to_vec(&HttpReply {
            request,
            msg: "Hello from rust!".to_string(),
          })?);
      }

      ResponseBuilder::new()
        .mimetype("text/html")
        .status(404)
        .body(Vec::new())
    })
    .menu(menu::get_menu())
    .on_menu_event(|event| {
      println!("{:?}", event.menu_item_id());
    })
    .system_tray(
      SystemTray::new().with_menu(
        SystemTrayMenu::new()
          .add_item(CustomMenuItem::new("toggle", "Toggle"))
          .add_item(CustomMenuItem::new("new", "New window"))
          .add_item(CustomMenuItem::new("icon_1", "Tray Icon 1"))
          .add_item(CustomMenuItem::new("icon_2", "Tray Icon 2"))
          .add_item(CustomMenuItem::new("exit_app", "Quit")),
      ),
    )
    .on_system_tray_event(|app, event| match event {
      SystemTrayEvent::LeftClick {
        position: _,
        size: _,
        ..
      } => {
        let window = app.get_window("main").unwrap();
        window.show().unwrap();
        window.set_focus().unwrap();
      }
      
      _ => {}
    })
    // assign our generated "handler" to the chain
    .invoke_handler(
      // piece together application logic
      tauri::generate_handler![
        // attach the commands
        cmd::log_operation,
        // cmd::perform_request,
        cmd::greet,
        cmd::log111,
        cmd::get_actix_graphql_endpoint
      ],
    )
    .build(
      // put it all together
      tauri::generate_context!(),
    )
    .expect("error while building tauri application");

  app.run(|app_handle, e| match e {
    // Application is ready (triggered only once)
    RunEvent::Ready => {
      let app_handle = app_handle.clone();
      // launch a new thread so it doesnt block any channel
      async_runtime::spawn(async move {
        let app_handle = app_handle.clone();
        app_handle
          .global_shortcut_manager()
          .register("CmdOrCtrl+1", move || {
            let app_handle = app_handle.clone();
            let window = app_handle.get_window("main").unwrap();
            window.set_title("New title!").unwrap();
          })
          .unwrap();
      });
    }

    // Triggered when a window is trying to close
    RunEvent::CloseRequested { label, api, .. } => {
      let app_handle = app_handle.clone();
      let window = app_handle.get_window(&label).unwrap();
      // use the exposed close api, and prevent the event loop to close
      api.prevent_close();
      // ask the user if he wants to quit
      // we need to run this on another thread because this is the event loop callback handler
      // and the dialog API needs to communicate with the event loop.
      std::thread::spawn(move || {
        tauri::api::dialog::ask(
          Some(&window),
          "Tauri API",
          "Are you sure that you want to close this window?",
          move |answer| {
            if answer {
              app_handle.get_window(&label).unwrap().close().unwrap();
            }
          },
        );
      });
    }

    // Keep the event loop running even if all windows are closed
    // This allow us to catch system tray events when there is no window
    RunEvent::ExitRequested { api, .. } => {
      api.prevent_exit();
    }
    _ => {}
  })
}
